package playGround.dungeon;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import playGround.border.Border;
import playGround.border.Door;
import playGround.border.Wall;
import playGround.square.OrdinarySquare;
import playGround.square.Rock;
import playGround.square.Scale;
import playGround.square.Square;
import exceptions.IllegalPositionException;
import exceptions.SlipperinessOverflowException;

public class DungeonTest {
	private Level<Square> dungeon;
	private Position maxPosition;
	private Position position;
	private Square square;
	private Square square1;
	private Square square2;
	private Square square3;
	private Square square4;
	private Square square5;
	private Map<Direction, Border> borders;
	@Before
	public void setUp() throws Exception {
		maxPosition=new Position(100,100,0);
		dungeon= new Level<Square>(maxPosition);
		position = new Position(6,5,0);
		Border wall1=new Wall(false);
		Border wall2 = new Wall(false);
		borders = new HashMap<Direction, Border>();
		borders.put(Direction.FLOOR,wall1);
		borders.put(Direction.EAST, wall2);
		square = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders);
		Map<Direction,Border>borders6 = new HashMap<Direction, Border>();
		borders6.put(Direction.FLOOR, new Wall(false));
		borders6.put(Direction.FLOOR, new Wall(false));
		square1 = new OrdinarySquare(200,new BigDecimal(90), Scale.CELCIUS, borders6);
		Border wall3=new Wall(false);
		Border wall4 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall3);
		borders1.put(Direction.EAST, wall4);
		square2 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border wall = new Wall(false);
		borders5.put(Direction.FLOOR,wall);
		square3 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall5 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall5);
		square4 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders2);
		Map<Direction,Border>borders3 = new HashMap<Direction, Border>();
		Border door1=new Door();
		borders3.put(Direction.NORTH,door1);
		square5= new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders3);
	}

	@After
	public void tearDown() throws Exception {
	}
	@Test
	public void testInvariant(){
		assert(dungeon.hasValidSquaresOnPosition());
	}
	@Test(expected = IllegalArgumentException.class)
	public void canHaveMaxDimTest(){
		Position position = new Position(-5,20,0);
		new Level<Square>(position);
	}
	@Test(expected = IllegalArgumentException.class)
	public void addSquareNullTest(){
		dungeon.addSquare(position, null);
	}
	@Test(expected = IllegalArgumentException.class)
	public void addSquareAlreadyExistTest(){
		dungeon.addSquare(position, square);
		dungeon.addSquare(maxPosition, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPositionLevel(){
		Position position1 = new Position(0,0,0);
		dungeon.addSquare(position1, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPosition1(){
		Position position1 = new Position(2,2,2);
		dungeon.addSquare(position1, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPositionlargerMax(){
		Position position1 = new Position(200,1,0);
		dungeon.addSquare(position1, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPositionAlreadyExists(){
		Position position1 = new Position(6,5,0);
		dungeon.addSquare(position1, square);
		dungeon.addSquare(position1, square1);
	}
	@Test
	public void addSquareTest(){
		Position position1 = new Position(2,1,0);
		dungeon.addSquare(position1, square);
		assertTrue(dungeon.dungeonHasSquare(square));
	}
	@Test
	public void removeSquareTestOnPos(){
		Position position = new Position(3,2,0);
		dungeon.addSquare(position, square);
		dungeon.removeSquare(position);
		assertFalse(dungeon.positionHasSquare(position));
	}
	@Test
	public void removeSquareTestOnSquare(){
		Position position = new Position(4,2,0);
		dungeon.addSquare(position, square);
		dungeon.removeSquare(square);
		assertFalse(dungeon.dungeonHasSquare(square));
	}
	@Test(expected = SlipperinessOverflowException.class)
	public void addSquareSlipperyFail(){
		Border wall1=new Wall(true);
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall1);
		borders1.put(Direction.EAST, wall2);
		Square square2 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		dungeon.addSquare(new Position(2,2,0), square2);
	}
	@Test
	public void addSquareNextToEachOtherSameArea(){
		Position position = new Position(1,1,0);
		Position otherPosition = new Position(1,2,0);
		dungeon.addSquare(position, square);
		dungeon.addSquare(otherPosition, square1);
		assertEquals(250,square.getTemperature());
		assertEquals(250,square1.getTemperature());
		assertEquals(new BigDecimal(50.0).setScale(2),square.getHumidity());
		assertEquals(new BigDecimal(50.0).setScale(2),square1.getHumidity());
	}
	@Test
	public void addSquareNextToEachOtherWithBorder(){
		Border wall1=new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.SOUTH,wall1);
		Position position = new Position(1,3,0);
		Position otherPosition = new Position(1,2,0);
		Square square2 = new OrdinarySquare(200,new BigDecimal(10), Scale.CELCIUS, borders1);
		dungeon.addSquare(position, square);
		dungeon.addSquare(otherPosition, square2);
		assertEquals(300,square.getTemperature());
		assertEquals(200,square2.getTemperature());
		assertEquals(new BigDecimal(10.0).setScale(2),square.getHumidity());
		assertEquals(new BigDecimal(10.0).setScale(2),square2.getHumidity());
	}
	@Test
	public void addSquareSlipperyWork(){
		Border wall1=new Wall(false);
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall1);
		borders1.put(Direction.EAST, wall2);
		Square square2 = new OrdinarySquare(200,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border door1 = new Door();
		borders5.put(Direction.WEST,door1);
		Square square3 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall3 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall3);
		Square square4 = new OrdinarySquare(50,new BigDecimal(30), Scale.CELCIUS, borders2);
		Map<Direction,Border>borders4 = new HashMap<Direction, Border>();
		Border door = new Door();
		borders4.put(Direction.NORTH,door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders4);
		Map<Direction,Border>borders3 = new HashMap<Direction, Border>();
		Border wall4 = new Wall(true);
		borders3.put(Direction.WEST, wall4);
		Square square6 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders3);
		dungeon.addSquare(new Position(1,2,0), square2);
		dungeon.addSquare(new Position(1,3,0), square3);
		dungeon.addSquare(new Position(2,3,0), square4);
		dungeon.addSquare(new Position(2,1,0), square5);
		dungeon.addSquare(new Position(2,4,0), square6);
	}
	@Test
	public void testSamePosition(){
		dungeon.addSquare(position, square);
		Position position1 = new Position(6,5,0);
		assertTrue(position1.equals(position));
		Position position2 = new Position(5,6,0);
		assertEquals(position.hashCode(),position1.hashCode());
		assertNotSame(position1.hashCode(),position2.hashCode());
		assertEquals(true,dungeon.positionHasSquare(position1));
	}
	@Test
	public void calculateAreaTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		Set<Position> positionSet=new HashSet<Position>();
		Set<Position> allreadyDone=new HashSet<Position>();
		positionSet.add(position);
		positionSet.add(position1);
		positionSet.add(position2);
		assertEquals(dungeon.calculateArea(position2, allreadyDone),positionSet);
	}

	@Test
	public void temperatureChangeAfterAreaChange(){
		square4.setTemperature(400);
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(square2.getTemperature(),200);
		assertEquals(square4.getTemperature(),200);

	}
	@Test
	public void humidityChangeAfterAreaChange(){
		square4.setHumidity(new BigDecimal(40));
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(square2.getHumidity(),new BigDecimal(20).setScale(2));
		assertEquals(square4.getHumidity(),new BigDecimal(20).setScale(2));
	}

	@Test
	public void positionEqualsTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertTrue(dungeon.positionHasSquare(new Position(1,2,0)));
	}

	@Test
	public void temperatureInbalanceAfterDoorChange(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		square5.setTemperature(400);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(200,square3.getTemperature());
		assertEquals(200,square5.getTemperature());
	}
	@Test
	public void humidityInbalanceAfterDoorChange(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		square5.setHumidity(new BigDecimal(40));
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(square3.getHumidity(),new BigDecimal(20).setScale(2));
		assertEquals(square5.getHumidity(),new BigDecimal(20).setScale(2));
	}

	@Test
	public void borderConflictTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Border testWall= new Wall(false);
		square2.addBorder(Direction.NORTH, testWall );
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		assertEquals(square2.getBorder(Direction.SOUTH), square3.getBorder(Direction.NORTH));
	}
	@Test
	public void borderAddTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position1, position, new Wall(false));
		assertFalse(square2.canNavigateTo(square4));
	}
	@Test
	public void borderRemovalTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position, position1, new Wall(false));
		dungeon.removeBorderBetweenTwoSquares(position1, position);
		assertTrue(square2.canNavigateTo(square4));
	}
	@Test(expected= IllegalArgumentException.class)
	public void borderRemovalExceptionTest(){
		Position position1=new Position(1,2,0);
		Position position2=new Position(1,3,0);
		dungeon.addSquare(position1, square2);
		dungeon.removeBorderBetweenTwoSquares(position1, position2);
	}
	@Test(expected = IllegalArgumentException.class)
	public void borderRemovalExceptionTest2(){
		Position position1=new Position(1,2,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position2, square2);
		dungeon.addSquare(position1, square3);
		dungeon.removeBorderBetweenTwoSquares(position1, position2);
	}
	@Test
	public void temperatureInbalanceAfterBorderRemovalTest(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position, position1, new Wall(false));
		square3.setTemperature(400);
		dungeon.removeBorderBetweenTwoSquares(position, position1);
		assertEquals(200, square2.getTemperature());
		assertEquals(200, square4.getTemperature());
	}
	@Test
	public void minMaxTemperatureAfterDoorChange(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		square5.setMaxTemperature(10000);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(square3.getMaxTemperature(),10000);
	}
	@Test
	public void minMaxTemperatureAfterAddingSquare(){
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		square4.setMaxTemperature(10000);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(10000,square2.getMaxTemperature());
	}
	@Test
	public void filterSquaresOnConditionTest() throws SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException{
		Level<Square> dungeon4 = new Level<Square>(maxPosition);
		dungeon4.addSquare(position, square);
		Set<Square> test = dungeon4.filterSquaresOnCondition("getTemperature > 10");
		assertEquals(test.size(),1);
	}
	@Test
	public void getSquaresOnConditionTestFail(){
		CompositeDungeon<Square> dungeon4 = new CompositeDungeon<Square>(maxPosition);
		Level<Square> dungeon5 = new Level<Square>(maxPosition);
		Position position = new Position(0,0,0);
		dungeon4.addSubDungeon(dungeon5,position);
		Position positionSquare = new Position(3,3,0);
		dungeon5.addSquare(positionSquare, square);
		Set<Square> test = dungeon4.getSquaresOnConditionRockTemperature();
		assertEquals(test.size(),0);
	}
	@Test
	public void getSquaresOnConditionTestLevel(){
		Level<Square> dungeon4 = new Level<Square>(maxPosition);
		Position positionSquare = new Position(3,2,0);
		dungeon4.addSquare(positionSquare, square);
		Position positionRock = new Position(3,3,0);
		Rock square1 = new Rock();
		dungeon4.addSquare(positionRock, square1);
		Set<Square> test = dungeon4.getSquaresOnConditionRockTemperature();
		assertEquals(test.size(),1);
	}
	@Test
	public void CalculateTemperatureRock(){
		Level<Square> dungeon4 = new Level<Square>(maxPosition);
		Position positionSquare = new Position(3,2,0);
		dungeon4.addSquare(positionSquare, square);
		Position positionRock = new Position(3,3,0);
		Rock square1 = new Rock();
		dungeon4.addSquare(positionRock, square1);
		assertEquals(300,square1.getTemperature());
	}
	@Test
	public void getSquaresOnConditionTest(){
		CompositeDungeon<Square> dungeon4 = new CompositeDungeon<Square>(maxPosition);
		Level<Square> dungeon5 = new Level<Square>(maxPosition);
		Position position = new Position(0,0,0);
		dungeon4.addSubDungeon(dungeon5,position);
		Position positionSquare = new Position(3,2,0);
		dungeon5.addSquare(positionSquare, square);
		Position positionRock = new Position(3,3,0);
		Rock square1 = new Rock();
		dungeon5.addSquare(positionRock, square1);
		Set<Square> test = dungeon4.getSquaresOnConditionRockTemperature();
		assertEquals(test.size(),1);
	}
	@Test
	public void filterSquaresOnConditionHeatDamage(){
		CompositeDungeon<Square> dungeon4 = new CompositeDungeon<Square>(maxPosition);
		Level<Square> dungeon5 = new Level<Square>(maxPosition);
		Position position = new Position(0,0,0);
		dungeon4.addSubDungeon(dungeon5,position);
		Position positionSquare = new Position(3,2,0);
		dungeon5.addSquare(positionSquare, square);
		Position positionRock = new Position(3,3,0);
		Rock square1 = new Rock();
		dungeon5.addSquare(positionRock, square1);
		Set<Square> test = null;
		test = dungeon4.filterSquaresOnCondition("getHeatDamage >= 10");
		assertEquals(test.size(),2);
	}
	@Test
	public void filterSquaresOnConditionSlippery() throws SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException{
		Level<Square> dungeon5 = new Level<Square>(maxPosition);
		Position positionSquare = new Position(3,2,0);
		Border wall21=new Wall(true);
		OrdinarySquare square7 = new OrdinarySquare();
		square7.addBorder(Direction.FLOOR, wall21);
		Position position = new Position(4,4,0);
		Position position1 = new Position(4,5,0);
		Position position2 = new Position(4,3,0);
		Position position3 = new Position(3,4,0);
		dungeon5.addSquare(position3, square1);
		dungeon5.addSquare(position2, square2);
		dungeon5.addSquare(position1, square3);
		dungeon5.addSquare(position, square4);
		dungeon5.addSquare(positionSquare, square);
		Position positionRock = new Position(3,3,0);
		Rock square1 = new Rock();
		dungeon5.addSquare(positionRock, square1);
		Set<Square> test = dungeon5.filterSquaresOnCondition("getSlippery == false");
		assertEquals(test.size(),6);
		
	}
	@Test
	public void borderConflictTestWithNeighbouringDungeons(){
		CompositeDungeon<Square> dungeon1 = new CompositeDungeon<Square>(maxPosition);
		Level<Square> dungeon2 = new Level<Square>(new Position(2,2,0));
		dungeon1.addSubDungeon(dungeon2, new Position(0,0,0));
		Level<Square> dungeon3= new Level<Square>(new Position(2,2,0));
		dungeon1.addSubDungeon(dungeon3, new Position(0,3,0));
		Map<Direction,Border> borders= new HashMap<Direction,Border>();
		borders.put(Direction.NORTH,new Wall(false));
		Square square3=new OrdinarySquare(40, new BigDecimal(0), Scale.CELCIUS,borders);
		Square square1 = new OrdinarySquare();
		Square square2=new OrdinarySquare();
		dungeon2.addSquare(new Position(1,2,0), square1);
		dungeon2.addSquare(new Position(2,2,0), square2);
		dungeon3.addSquare(new Position(1,0,0), square3);
		assertEquals(square3.getBorder(Direction.NORTH),square1.getBorder(Direction.SOUTH));

	}

    @Test(expected=IllegalPositionException.class)
    public void xxxTest(){
		CompositeDungeon<Square> dungeon1 = new CompositeDungeon<Square>(new Position(50,50,50));
		CompositeDungeon<Square> dungeon2=new CompositeDungeon<Square>(new Position(20,20,20));
		Level<Square> level = new Level<Square>(new Position(4,4,0));
		level.addSquare(new Position(0,2,1), new OrdinarySquare());
		dungeon2.addSubDungeon(level, new Position(1,1,2));
		dungeon1.addSubDungeon(dungeon2, new Position(2,1,1));

	}

}
